#include <sstream>
#include <iostream>
#include <string>

#include "Appointment_t.h"
#include "Calendar_t.h"

Calendar_t::Calendar_t()
{
}

Calendar_t::~Calendar_t()
{
	removeAll();
}

Calendar_t::Calendar_t(const Calendar_t& other)
	: m_appointments(other.m_appointments)
{
}

Calendar_t& Calendar_t::operator=(const Calendar_t& other)
{
	if(this != &other)
		m_appointments = other.m_appointments;

	return *this;
}

bool Calendar_t::insert(Appointment_t* const appointment)
{
	if(intersects(*appointment))
		return false; // Appointment found

	// Insert appointment
	m_appointments.insert(std::make_pair(appointment->startTime(), appointment));
	return true;
}

bool Calendar_t::remove(const Appointment_t& appointment, bool deletePtr)
{
	AppointmentsContainer::iterator it = m_appointments.find(appointment.startTime());
	if(it == m_appointments.end())
		return false; // Appointment not found

	// Delete if requested
	if(deletePtr)
		delete it->second;

	// Remove appointment
	m_appointments.erase(it);
	return true;
}

void Calendar_t::removeAll(bool deletePtrs)
{
	if(deletePtrs)
	{
		// Delete each appointment ptr
		AppointmentsContainer::iterator it = m_appointments.begin();
		for(; it != m_appointments.end(); it++)
			delete it->second;
	}

	// Clear appointments
	m_appointments.clear();
}

const Appointment_t& Calendar_t::find(const DayTime_t& startTime) const
{
	Appointment_t* appointment = lookup(startTime);
	if(appointment == NULL)
		return Appointment_t::invalid; // Appointment not found

	// Return found appointment
	return *appointment;
}

bool Calendar_t::duplicate(const Appointment_t& appointment, 
						   const DayTime_t& startTime)
{
	// Clone given appointment
	Appointment_t* clone = appointment.clone();

	// Calculate end time according to duration
	const DayTime_t& span = clone->endTime() - clone->startTime();
	const DayTime_t& endTime = startTime + span;

	// Set time
	if(!clone->setTime(startTime, endTime))
	{
		delete clone;
		return false;
	}

	// Attempt insertion
	if(!insert(clone))
	{
		delete clone;
		return false;
	}

	return true;
}

Appointment_t* Calendar_t::lookup(const DayTime_t& startTime) const
{
	AppointmentsContainer::const_iterator it = m_appointments.find(startTime);
	if(it == m_appointments.end())
		return NULL; // Appointment not found

	// Return found appointment
	return it->second;
}

bool Calendar_t::intersects(const Appointment_t& appointment) const
{
	// Find appointment
	AppointmentsContainer::const_iterator it = m_appointments.begin();
	for(; it != m_appointments.end(); it++)
	{
		if(*it->second == appointment)
			return true;
	}

	return false;
}

std::ostream& operator<<(std::ostream& output, const Calendar_t& cal)
{	
	std::ostringstream os;
	Calendar_t::AppointmentsContainer::const_iterator it = cal.m_appointments.begin();
	Day_t lastDay = Day_t::Undefined;

	while(it != cal.m_appointments.end())
	{
		const Day_t& day=(it->second)->startTime().day();

		// print new day header
		if(lastDay != day)
		{
			os << std::endl;
			os << (it->second)->startTime();
			lastDay = day;
		}

		// print appointment hours and subject
		os << *it->second << std::endl;
		it++;
	}
	return output << os.str();
}

